#include "dbuslog.hpp"
#include "common.hpp"
#include <qvariant.h>
DbusLog::DbusLog(QObject *parent)
{

}

DbusLog::DbusLog(const QString &prefix,const QString &filename,QObject *parent) : QObject(parent)
  ,m_path()
  ,m_prefix(prefix),m_file(Q_NULLPTR)
{
    m_path = getLogPath();
    if(!filename.isEmpty ())
    {
        QDir dir(m_path);
        QString filepath = dir.filePath (filename);
        m_file = new QFile(filepath);
        if(!m_file->open (QFile::Text|QFile::WriteOnly))
        {
            delete m_file;
            m_file = Q_NULLPTR;
        }
    }
}

QByteArray DbusLog::data()
{
    return m_message;
}

DbusLog::~DbusLog()
{
    if(m_file!=Q_NULLPTR)
    {
        m_file->close ();
        delete m_file;
        m_file = Q_NULLPTR;
    }
}
bool DbusLog::checkFile()
{
    if(m_file!=Q_NULLPTR)
    {
        if(m_file->size ()<1024*1024)
        {
            return true;
        }
        m_file->close ();
        delete m_file;
        m_file = Q_NULLPTR;
    }

    QDateTime t = QDateTime::currentDateTime ();
    QDir dir(m_path);
    QString filename = t.toString ("yyMMdd_hhmmsszzz.log");
    m_file = new QFile(dir.filePath (m_prefix+filename));
    if(!m_file->open (QFile::Text|QFile::WriteOnly))
    {
        delete m_file;
        m_file = Q_NULLPTR;
        return false;
    }
    emit logNameChange(m_path,m_prefix,dir.absoluteFilePath (m_prefix+filename));
    return true;
}
DbusLog &DbusLog::operator<<(QChar t)
{
    m_message.append(t);
    return *this;
}

DbusLog &DbusLog::operator<<(bool t)
{
    m_message.append((t) ? "true" : "false");
    return *this;
}
DbusLog &DbusLog::operator<<(char t)
{
    m_message.append(QString(t));
    return *this;
}

DbusLog &DbusLog::operator<<(uchar t)
{
    m_message.append(QString(t));
    return *this;
}
DbusLog &DbusLog::operator<<(signed short t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(unsigned short t)
{

        m_message.append(QString::number(t));

    return *this;
}
DbusLog &DbusLog::operator<<(signed int t)
{
        m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(unsigned int t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(signed long t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(unsigned long t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(qint64 t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(quint64 t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(float t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(double t)
{
    m_message.append(QString::number(t));
    return *this;
}
DbusLog &DbusLog::operator<<(const char *t)
{
    m_message.append(QString(t));
    return *this;
}
DbusLog &DbusLog::operator<<(const QString &t)
{
    QString t0 = t;
    t0 = t0.replace ("\r","\\r");
    t0 = t0.replace ("\n","\\n");
    m_message.append(t0);
    return *this;
}

DbusLog &DbusLog::operator<<(const QByteArray &t)
{
    QByteArray t0 = t;
    t0 = t0.replace ("\r","\\r");
    t0 = t0.replace ("\n","\\n");
    m_message.append(t0);
    return *this;
}

DbusLog &DbusLog::operator<<(const QDBusMessage &t)
{
    if(t.type () == QDBusMessage::ErrorMessage)
    {
        *this<<"error:";
        *this<<t.errorMessage ();
    }else
    {
        bool isfrish = true;
        for (const QVariant &busArg : t.arguments ())
        {
            if(!isfrish)
            {
                *this<<", ";
            }
            isfrish = false;
            *this<<busArg;
        }
    }
    return *this;
}

DbusLog &DbusLog::operator<<(const QDBusArgument &busArg)
{
    bool frish =true;
    QString busSig = busArg.currentSignature();
    bool doIterate = false;
    QDBusArgument::ElementType elementType = busArg.currentType();

    switch (elementType) {
    case QDBusArgument::BasicType:
    case QDBusArgument::VariantType:
        *this<<busArg.asVariant();
        break;
    case QDBusArgument::StructureType:
        busArg.beginStructure();
        doIterate = true;
        *this<<'(';
        frish =true;
        while (!busArg.atEnd ()) {
            if(!frish)
            {
               *this<<',';
            }
            frish = false;
            *this<<busArg.asVariant();
        }
        busArg.endStructure ();
        *this<<')';
        break;
    case QDBusArgument::ArrayType:
        busArg.beginArray();
        *this<<'[';
        frish =true;
        while (!busArg.atEnd ()) {
            if(!frish)
            {
               *this<<',';
            }
            frish = false;
            *this<<busArg.asVariant();
        }
        busArg.endArray ();
        *this<<']';
        break;
    case QDBusArgument::MapType:
        busArg.beginMap();
        *this<<'{';
        frish =true;
        while (!busArg.atEnd ()) {
            busArg.beginMapEntry();
            if(!frish)
            {
               *this<<',';
            }
            frish = false;
            *this<<busArg.asVariant();
            *this<<" = ";
            *this<<busArg.asVariant();;
            busArg.endMapEntry();
        }
        busArg.endMap ();
        *this<<'}';
        break;
    case QDBusArgument::UnknownType:
    default:
        *this<<"<ERROR - Unknown Type>";
        break;
    }
    return *this;
}

DbusLog &DbusLog::operator<<(const QVariant &arg)
{
    int argType = arg.userType();

    if (argType == QVariant::StringList) {
        *this<<"[";
        const QStringList list = arg.toStringList();
        bool isfrish = true;
        for (const QString &item : list)
        {
            if(!isfrish)
            {
                *this<<", ";
            }
            isfrish = false;
            *this<<'\"';
            *this<<item;
            *this<<'\"';
        }
        *this<<"]";
    } else if (argType == QVariant::ByteArray) {
        *this<<"[";
        QByteArray list = arg.toByteArray();
        *this<<list.toHex (' ');
        *this<<"]";
    } else if (argType == QVariant::List) {
        *this<<"[";
        const QList<QVariant> list = arg.toList();
        bool isfrish = true;
        for (const QVariant &item : list) {
            if(!isfrish)
            {
                *this<<", ";
            }
            isfrish = false;
             *this<<item;
        }
        *this<<"]";
    } else if (argType == QMetaType::Char || argType == QMetaType::Short || argType == QMetaType::Int
               || argType == QMetaType::Long || argType == QMetaType::LongLong) {
        *this<<QString::number(arg.toLongLong());
    } else if (argType == QMetaType::UChar || argType == QMetaType::UShort || argType == QMetaType::UInt
               || argType == QMetaType::ULong || argType == QMetaType::ULongLong) {
        *this<<QString::number(arg.toULongLong());
    } else if (argType == QMetaType::Double) {
        *this<<QString::number(arg.toDouble());
    } else if (argType == QMetaType::Bool) {
        if(arg.toBool())
        {
            *this<<"true";
        }else
        {
            *this<<"false";
        }
    } else if (argType == qMetaTypeId<QDBusArgument>()) {
        *this<<qvariant_cast<QDBusArgument>(arg);
    } else if (argType == qMetaTypeId<QDBusObjectPath>()) {
        const QString path = qvariant_cast<QDBusObjectPath>(arg).path();
        *this<<"[ObjectPath: ";
        *this<<path;
        *this<<']';
    } else if (argType == qMetaTypeId<QDBusSignature>()) {
        *this<<"[Signature: ";
        *this<<qvariant_cast<QDBusSignature>(arg).signature();
        *this<<']';
    } else if (argType == qMetaTypeId<QDBusUnixFileDescriptor>()) {
        *this<<"[Unix FD: ";
        if(qvariant_cast<QDBusUnixFileDescriptor>(arg).isValid())
        {
            *this<<"valid";
        }else
        {
            *this<<"not valid";
        }
    } else if (argType == qMetaTypeId<QDBusVariant>()) {
        const QVariant v = qvariant_cast<QDBusVariant>(arg).variant();
        *this<<"[Variant";
        int vUserType = v.userType();
        if (vUserType != qMetaTypeId<QDBusVariant>()
                && vUserType != qMetaTypeId<QDBusSignature>()
                && vUserType != qMetaTypeId<QDBusObjectPath>()
                && vUserType != qMetaTypeId<QDBusArgument>())
        {
            *this<<"_";
            *this<<v.typeName();
        }
        *this<<": ";
        *this<<v;
        *this<<"]";
    } else if (arg.canConvert(QVariant::String)) {
        *this<<'\"';
        *this<<arg.toString();
        *this<<'\"';
    } else {
         *this<<"[Unknown:";
        *this<<arg.typeName();
        *this<<"]";
    }
    return *this;
}

DbusLog &DbusLog::operator<<(const DVariant &t)
{
    bool ok=false;
    if(t.m_type == QMetaType::UnknownType)
    {
        return *this;
    }else if(t.m_type == DBUS_TYPE_DICT)
    {
        dict use = t.getDict(&ok);
        if(!ok)
        {
            return *this;
        }
        if(use.m_Subtypes.count ()<2)
        {
            return *this;
        }
        *this<<"{";
        int isFrish = true;
        for(int i=0;i<use.Values.count () && i<use.keys.count ();i++) {
            if(i>0)
            {
                *this<<",";
            }
            isFrish = false;
            *this<<use.keys.at (i);
            *this<<" = ";
            *this<<use.Values.at (i);
        }
        *this<<"}";
        return *this;
    }else if(t.m_type == DBUS_TYPE_ARRAR)
    {
        array use = t.getArray (&ok);
        if(!ok)
            return *this;

        if(use.m_Subtypes.count ()<1)
            return *this;
        *this<<"[";
        int isFrish = true;
        foreach (auto var, use.Values) {
            if(!isFrish)
            {
                *this<<",";
            }
            isFrish = false;
            *this<<var;
        }
        *this<<"]";
        return *this;
    }else if(t.m_type == DBUS_TYPE_STRUCT)
    {
        structs use = t.getStruct (&ok);
        if(!ok)
            return *this;
        *this<<"(";
        int isFrish = true;
        foreach (auto var, use.Values) {
            if(!isFrish)
            {
                *this<<",";
            }
            isFrish = false;
            *this<<var;
        }
        *this<<")";
        return *this;
    }else if(t.m_type == QMetaType::QByteArray)
    {
        if(t.m_showModel == ShowModelHex)
        {
            *this<<QString(t.m_Values.toByteArray ().toHex (' ')).toUpper ();
        }else
        {
            *this<<"\"";
            QByteArray str = t.m_Values.toByteArray ();
            str = str.replace ("\r","\\r");
            str = str.replace ("\n","\\n");
            *this<<str;
            *this<<"\"";
        }
        return *this;
    }else if(t.m_type == QMetaType::Bool)
    {
        if(t.m_Values.toBool ())
        {
            *this<<"Ture";
        }else
        {
            *this<<"False";
        }
        return *this;
    }else if(t.m_type == QMetaType::UChar
             || t.m_type == QMetaType::UShort
             || t.m_type == QMetaType::UInt
             || t.m_type == QMetaType::ULongLong
             )
    {
         if(t.m_showModel == ShowModelHex)
         {
             int size = QMetaType::sizeOf (t.m_type)*2;
            *this<<QString::number (t.m_Values.toULongLong (),16).right (size);
         }else
         {
             *this<<QString::number(t.m_Values.toULongLong ());
         }
         return *this;
    }else if(t.m_type == QMetaType::Char
             || t.m_type == QMetaType::Short
             || t.m_type == QMetaType::Int
             || t.m_type == QMetaType::LongLong
             )
    {
         if(t.m_showModel == ShowModelHex)
         {
             int size = QMetaType::sizeOf (t.m_type)*2;
            *this<<QString::number (t.m_Values.toULongLong (),16).right (size);
         }else
         {
             *this<<QString::number(t.m_Values.toLongLong ());
         }
         return *this;
    }else if(t.m_type == QMetaType::Double)
    {
        *this<<t.m_Values.toString ();
        return *this;
    }else if(t.m_type == QMetaType::QString)
    {
        *this<<"\"";
        QString str = t.m_Values.toString ();
        str = str.replace ("\r","\\r");
        str = str.replace ("\n","\\n");
        *this<<str;
        *this<<"\"";
        return *this;
    }else if(t.m_type == qMetaTypeId<QDBusObjectPath>())
    {
        *this<<"\"";
        *this<<t.m_Values.toString ();
        *this<<"\"";
        return *this;
    }else if(t.m_type == qMetaTypeId<QDBusSignature>())
    {
        *this<<"\"";
        *this<<t.m_Values.toString ();
        *this<<"\"";
        return *this;
    }else if(t.m_type == qMetaTypeId<QDBusUnixFileDescriptor>())
    {
        *this<<QString::number ((int32_t)t.m_Values.toLongLong ());
        return *this;
    }else if(t.m_type == qMetaTypeId<QDBusVariant>())
    {
        *this<<"V\"";
        *this<<t.m_Values.toString ();
        *this<<"\"";
        return *this;
    }
    return *this;
}

DbusLog &DbusLog::operator<<(const QDateTime &t)
{
    QString str = t.toString ("hh:mm:ss.zzz");
    m_message.append(str);
    return *this;
}

DbusLog &DbusLog::operator<<(QTextStreamFunction f)
{
    Q_UNUSED(f);
    checkFile();
    m_file->write (m_message);
    m_file->write ("\n");
    m_message.clear ();
    return *this;
}

DbusLog &runLog(const QString &filename)
{
    static DbusLog g_runLog("runlog_",filename);
    return g_runLog;
}

DbusLog &signalLog(QString filename)
{
    static DbusLog g_signalLog("signallog_",filename);
    return g_signalLog;
}

QString getLogPath()
{
    QString strPath = qApp->applicationDirPath ();
    QDir dir(strPath);
    if(strPath.endsWith ("Contents/MacOS"))
    {
        dir.cdUp ();
        dir.cdUp ();
        dir.cdUp ();
    }
    strPath = dir.filePath ("log");
    if(!dir.exists ("log"))
    {
        dir.mkdir ("log");
    }
    return QString(strPath);
}

